import L from 'leaflet'
import 'leaflet/dist/leaflet.css'
import * as d3 from 'd3'
import * as d3Voronoi from 'd3-geo-voronoi'
import { CityComparison } from '@/assets/js/geoMap/CityComparison.js'
export class LeafletMap {
  lmap = ''
  chart = {}
  jsonData = ''
  color = function(i) {
    // let mycolor = ['#949483', '#f47b7b', '#9f1f5c', '#ef9020', '#00af3e',
    //   '#85b7e2', '#29245c', '#ffd616', '#e5352b', '#e990ab', '#0081b4',
    //   '#96cbb3', '#91be3e', '#39a6dd']
    let newColor = ['#8db5d3', '#f7c3b0', '#a1ede9', '#ffa05a', '#c492fd', '#3baeff',
      '#aed8ff', '#69bcaf', '#f2e280', '#d4cff7', '#fa8170', '#8f89ff', '#93d47f', '#ffc34e']
    return newColor[i % 14]
  }
  constructor(divId, vue) {
    this.divId = divId
    this.vue = vue
  }
  initMap() {
    this.lmap = L.map(this.divId).setView([38.860339, 107.3683244], 4)
    L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', {
      maxZoom: 18,
      id: 'mapbox/streets-v11',
      tileSize: 512,
      zoomOffset: -1,
      accessToken: 'pk.eyJ1Ijoic3d1c3R6aG91eWFuZyIsImEiOiJja2h5bjR3eW4wN3JjMzBwZXpiOHZlbThhIn0.keNHzlQnb6RqUaAqKXmtlg'
    }).addTo(this.lmap)
    L.svg().addTo(this.lmap)

    let lmap = this.lmap
    let that = this

    this.lmap.on('zoom', function() {
      that.reset(this)
    })
    this.chart.projectPoint = function(x, y) {
      const point = lmap.latLngToLayerPoint(new L.LatLng(y, x))
      this.stream.point(point.x, point.y)
    }
    this.chart.projection = d3.geoTransform({
      point: this.chart.projectPoint
    })

    this.chart.pathGenerator = d3.geoPath()
      .projection(this.chart.projection)

    this.chart.voronoi = d3Voronoi.geoVoronoi()
      .x(d => d.lon)
      .y(d => d.lat)

    this.chart.svg = d3.select('#' + this.divId).select('svg')
  }

  initProvinceMap(jsonData) {
    this.jsonData = jsonData

    let map = this.chart.svg.append('g')
      .attr('class', 'provinceMap')

    this.chart.provinceMap = map.selectAll('.province')
      .data(jsonData.features)
      .enter()
      .append('g')
      .attr('class', 'province')

    this.chart.provinceMap.append('path')
      .attr('d', this.chart.pathGenerator)
      .style('stroke', 'black')
      .style('stroke-width', 1)
      .style('fill', 'none')
      .style('opacity', 1)
  }

  initCityMap(jsonData) {
    let map = this.chart.svg.append('g')
      .attr('class', 'cityMap')

    this.chart.cityMap = map.selectAll('.path')
      .data(jsonData.features)
      .enter()
      .append('g')
      .attr('class', 'path')

    this.chart.cityMap.append('path')
      .attr('d', this.chart.pathGenerator)
      .style('stroke', 'black')
      .style('stroke-width', 1)
      .style('fill', 'none')
      .style('opacity', 0)
  }

  renderCityPoints(cities) {
    this.chart.gCities = this.chart.svg.append('g')
      .attr('class', 'cities')
      .selectAll('.city')
      .data(cities)
      .enter()
      .append('g')
      .attr('class', 'city')
      .attr('id', d => d.name)
      .style('stroke', 'red')
      .style('fill', 'red')

    this.chart.gCities.append('circle')
      .attr('cx', d => {
        d._oldX = this.lmap.latLngToLayerPoint([d.lat, d.lon]).x
        d._oldY = this.lmap.latLngToLayerPoint([d.lat, d.lon]).y
        return this.lmap.latLngToLayerPoint([d.lat, d.lon]).x
      })
      .attr('cy', d => this.lmap.latLngToLayerPoint([d.lat, d.lon]).y)
      .attr('r', 1)

    this.chart.gCities.append('text')
      .attr('transform', d => 'translate(' + d._oldX +
        ',' + d._oldY + ')')
      .text(d => d.name)
      .attr('font-size', 8)
      .style('opacity', 0)
  }

  drawLegend() {
    let category = ['长江中游城市群', '哈长城市群', '成渝城市群', '长江三角洲城市群', '中原城市群',
      '北部湾城市群', '关中平原城市群', '呼包鄂榆城市群', '兰西城市群', '粤港澳大湾区', '京津冀城市群',
      '辽中南城市群', '山东半岛城市群', '海峡西岸城市群']
    let legendWidth = 10
    let legendHeight = 10
    let dom = document.getElementById(this.divId)
    let divHeight = dom.offsetHeight
    let divWidth = dom.offsetWidth
    let yStart = divHeight - legendHeight * category.length - 15
    this.chart.svg.select('.legends').remove()
    let legends = this.chart.svg
      .append('g')
      .attr('class', 'legends')

    let legend = legends.selectAll('.legend')
      .data(category)
      .enter()
      .append('g')
      .attr('class', 'legend')

    legend.append('rect')
      .attr('x', divWidth - 100)
      .attr('y', (_, i) => {
        return yStart + legendHeight * i
      })
      .attr('width', legendWidth)
      .attr('height', legendHeight - 3)
      .style('fill', (_, i) => this.color(i))
      .style('opacity', 0.5)

    legend.append('text')
      .attr('x', divWidth - 100 + legendWidth * 1.2)
      .attr('y', (_, i) => (yStart + 1 + legendHeight * i + legendHeight / 2))
      .text(String)
      .attr('font-size', 10)
  }

  /**
   * 添加voronoi图的边界
   * @param {*} outline
   */
  renderOutline(outline) {
    let goutline = this.chart.svg.selectAll('.outline')
      .data(outline.features)
      .enter()
      .append('g')
      .attr('class', 'outline')
      .append('clipPath')
      .attr('id', 'clipPath')

    this.chart.boundrayPath = goutline.append('path')
      .attr('d', this.chart.pathGenerator)
      .style('fill', 'red')
      .style('stroke', 'black')
      .style('stroke-width', 1)
  }
  /**
   * 绘制voronoi图
   */
  renderVoronoi(data) {
    this.chart.svg.select('.gConcentric').selectAll('.concentric').remove()
    this.chart.svg.selectAll('.city').each(function(d) {
      d3.select(this).select('.' + d.city_name).remove()
    })
    this.containCity = []
    let extraCity = ['双鸭山市', '大兴安岭地区', '延边朝鲜族自治州', '鸡西市', '黑河市', '台湾省']
    let vdata = this.chart.voronoi(data)

    let voData = vdata.polygons().features.map(d => {
      let thisD = ''
      if ((extraCity.indexOf(d.properties.site.city_name) === -1)) {
        thisD = JSON.parse(JSON.stringify(d))
        if (d.properties.site.city_name === '陵水黎族自治县') {
          let coord = d.geometry.coordinates
          for (let i = 0; i < coord[0].length; ++i) {
            // console.log(coord[0][i][0], coord[0][i][1])
            if (coord[0][i][0] === 180) {
              coord[0][i][0] = 0.0000
            }
            if (coord[0][i][1] === 90) {
              coord[0][i][1] = 0.0000
            }
          }
          d.geometry.coordinates = coord
          thisD = JSON.parse(JSON.stringify(d))
        }
      } else {
        let coord = d.geometry.coordinates
        for (let i = 0; i < coord[0].length; ++i) {
          if (coord[0][i][0] < 0) {
            coord[0][i][0] = 180.0000
          }
          if (coord[0][i][1] < 0) {
            coord[0][i][1] = 90.0000
          }
        }
        d.geometry.coordinates = coord
        thisD = JSON.parse(JSON.stringify(d))
      }
      return thisD
    })

    let gVoronoi = this.chart.svg.select('.voronois')
    if (gVoronoi.empty()) {
      gVoronoi = this.chart.svg.append('g')
        .attr('class', 'voronois')
        .attr('clip-path', 'url(#clipPath)')
    }

    let gv = gVoronoi.selectAll('.voronoi')
      .data(voData, d => d.properties.site.city_name)

    gv.transition()
      .duration(750)
      .style('fill', d => this.color(d.properties.site.category))
      .style('stroke', 'black')
      .style('stroke-width', 1)
      .style('opacity', 0.5)

    this.chart.voronoiMap = gv.enter()
      .append('path')
      .attr('class', 'voronoi')
      .attr('d', this.chart.pathGenerator)
      .attr('style', 'pointer-events: auto;')
      .on('click', (_, d) => {
        this.containCity = []
        this.comCityChart = []
        this.renderCityWind(d.properties.site.city_name)
      })
      .style('fill', d => {
        return this.color(d.properties.site.category)
      })
      .style('stroke', 'black')
      .style('stroke-width', 1)
      .style('opacity', 0.5)

    this.chart.voronoiMap.append('title')
      .text(d => d.properties.site.city_name)
  }

  renderCityWind(city) {
    this.chart.svg.selectAll('.city').each(function(d) {
      d3.select(this).select('.' + d.city_name).remove()
    })
    let gCities = this.chart.svg.select('.cities').selectAll('.city')
    // gCities.style('opacity', 0)
    let data = ''
    let targetCity = gCities.filter(d => {
      if (d.city_name === city) {
        data = JSON.parse(JSON.stringify(d))
        return d
      }
    })
    let selectedCity = []
    this.containCity = gCities.filter(d => {
      let distance = this.geoDistance(d.lon, d.lat, data.lon, data.lat)
      if (distance < 200) {
        selectedCity.push(d.city_name)
        return d
      }
    })

    targetCity.style('opacity', 1)
      .style('fill', 'red')
      .style('stroke', 'red')

    // this.lmap.panTo({'lon': data.lon, 'lat': data.lat})

    this.renderComparison(data)
    this.renderContainCompa(this.containCity)
    this.vue.chart.parallelChart.filterByCluster(selectedCity, 'city_name')
    this.vue.chart.steelChart.selectedCity(selectedCity)
  }

  renderComparison(data) {
    let gConcentric = ''
    if (this.chart.svg.select('.gConcentric').empty()) {
      gConcentric = this.chart.svg.append('g')
        .attr('class', 'gConcentric')
    } else {
      gConcentric = this.chart.svg.select('.gConcentric')
    }

    let radius = 200 // km
    let circle = d3.geoCircle()
      .center(d => {
        return [d.lon, d.lat]
      })
      .radius(radius / (6371 * Math.PI * 2) * 360)

    let bounds = this.chart.pathGenerator.bounds(circle(data))
    let r = (bounds[1][0] - bounds[0][0]) / 2

    let concentric = gConcentric.selectAll('.concentric')
      .data([data])

    concentric.transition()
      .duration(750)
      .attr('cx', d => {
        return this.lmap.latLngToLayerPoint([d.lat, d.lon]).x
      })
      .attr('cy', d => this.lmap.latLngToLayerPoint([d.lat, d.lon]).y)
      .attr('r', r)

    concentric.enter()
      .append('circle')
      .attr('class', 'concentric')
      .attr('cx', d => this.lmap.latLngToLayerPoint([d.lat, d.lon]).x)
      .attr('cy', d => this.lmap.latLngToLayerPoint([d.lat, d.lon]).y)
      .attr('r', r)
      .style('fill', 'none')
      .style('stroke', 'orange')
      .style('stroke-width', 2)
  }

  renderContainCompa(containCity) {
    let city = []
    let psfcMax = 0
    let tempMax = -273.15
    let tempMin = 1000
    let lmap = this.lmap
    let chart = this.chart
    containCity.each(function(d) {
      let minDis = Number.MAX_VALUE
      let dx = lmap.latLngToLayerPoint([d.lat, d.lon]).x
      let dy = lmap.latLngToLayerPoint([d.lat, d.lon]).y
      containCity.each(o => {
        if (d !== o) {
          let ox = lmap.latLngToLayerPoint([o.lat, o.lon]).x
          let oy = lmap.latLngToLayerPoint([o.lat, o.lon]).y
          let distance = Math.sqrt(Math.pow((dx - ox), 2) + Math.pow((dy - oy), 2))
          if (distance < minDis) {
            minDis = distance
          }
        }
      })
      let _d = JSON.parse(JSON.stringify(d))
      _d.startAngle = Math.PI
      _d.endAngle = Math.PI * 2

      if (minDis === Number.MAX_VALUE) {
        let radius = 200 // km
        let circle = d3.geoCircle()
          .center(d => {
            return [d.lon, d.lat]
          })
          .radius(radius / (6371 * Math.PI * 2) * 360)
        let bounds = chart.pathGenerator.bounds(circle(d))
        let r = (bounds[1][0] - bounds[0][0]) / 2
        _d.basicRadius = r
      } else {
        _d.basicRadius = minDis / 2
      }
      _d.position = [dx, dy]

      if (d['摄氏温度'] > tempMax) {
        tempMax = d['摄氏温度']
      }
      if (d['摄氏温度'] < tempMin) {
        tempMin = d['摄氏温度']
      }
      if (d['PSFC(Pa)'] > psfcMax) {
        psfcMax = d['PSFC(Pa)']
      }
      let comData = []
      let LEFTKEY = ['PM2.5(微克每立方米)', 'PM10(微克每立方米)', 'SO2(微克每立方米)',
        'NO2(微克每立方米)', 'CO(毫克每立方米)', 'O3(微克每立方米)'
      ]
      for (let key in d) {
        if (LEFTKEY.indexOf(key) !== -1) {
          comData.push({
            'name': key,
            'value': d[key]
          })
        }
      }
      _d.pollutantData = comData
      d3.select(this).select('.' + _d.city_name).remove()
      let container = d3.select(this).append('g')
        .attr('class', _d.city_name)

      _d.container = container
      city.push(_d)
    })
    for (let i = 0; i < city.length; ++i) {
      let config = {}
      config.name = city[i]['city_name']
      config.container = city[i].container
      config.basicRadius = city[i].basicRadius
      config.position = city[i].position

      config.pollutantData = city[i].pollutantData
      config.tempData = {
        'value': (city[i]['摄氏温度'] - tempMin) / ((tempMax - tempMin) + 5),
        'range': (tempMax - tempMin) + 5
      }
      config.rh = city[i]['RH(%)']
      config.wind = [{
        'windDirection': city[i]['风向'],
        'windSpeed': city[i]['风速']
      }]
      config.psfcData = {
        'value': city[i]['PSFC(Pa)'],
        'max': psfcMax
      }
      config.zoom = this.lmap.getZoom()
      let com = new CityComparison(config)
      com.draw()
    }
  }

  showSelectedCity(city) {
    this.chart.svg.select('.gConcentric').selectAll('.concentric').remove()
    this.chart.svg.selectAll('.city').each(function(d) {
      d3.select(this).select('.' + d.city_name).remove()
    })
    this.containCity = []

    this.chart.svg.selectAll('.voronoi')
      .style('opacity', 0)
      .filter(function(d) {
        if (city.indexOf(d.properties.site.city_name) !== -1) {
          return d
        }
      })
      .style('opacity', 0.5)
    this.chart.svg.selectAll('.city')
      .select('circle')
      .style('opacity', 0)
      .filter(function(d) {
        if (city.indexOf(d.city_name) !== -1) {
          return d
        }
      })
      .style('opacity', 1)
  }

  showTransferCity(city) {
    this.chart.svg.select('.gConcentric').selectAll('.concentric').remove()
    this.chart.svg.selectAll('.city').each(function(d) {
      d3.select(this).select('.' + d.city_name).remove()
    })
    this.containCity = []

    this.chart.svg.selectAll('.city')
      .filter(function(d) {
        if (city.indexOf(d.city_name) !== -1) {
          return d
        }
      })
      .select('circle')
      .transition()
      .duration(750)
      .attr('r', 5)
      .style('blue')
  }

  showClusterCity(clusters) {
    this.chart.svg.select('.gConcentric').selectAll('.concentric').remove()
    this.chart.svg.selectAll('.city').each(function(d) {
      d3.select(this).select('.' + d.city_name).remove()
    })
    this.containCity = []

    this.chart.svg.selectAll('.voronoi')
      .style('opacity', 0)
      .filter(function(d) {
        if (clusters.indexOf(d.properties.site.category) !== -1) {
          return d
        }
      })
      .style('opacity', 0.5)
    this.chart.svg.selectAll('.city')
      .select('circle')
      .style('opacity', 0)
      .filter(function(d) {
        if (clusters.indexOf(d.category) !== -1) {
          return d
        }
      })
      .style('opacity', 1)
  }

  reset(that) {
    // this.chart.provinceMap.select('path')
    //   .attr('d', this.chart.pathGenerator)

    // this.chart.cityMap.select('path')
    //   .attr('d', this.chart.pathGenerator)

    this.chart.boundrayPath
      .attr('d', this.chart.pathGenerator)

    this.chart.svg.selectAll('.voronoi').attr('d', this.chart.pathGenerator)

    this.chart.gCities.select('circle')
      .attr('cx', d => this.lmap.latLngToLayerPoint([d.lat, d.lon]).x)
      .attr('cy', d => this.lmap.latLngToLayerPoint([d.lat, d.lon]).y)
      .attr('r', 1 * that._zoom / 2)

    let concentric = this.chart.svg.select('.gConcentric').selectAll('.concentric')
    if (!concentric.empty()) {
      let data = concentric.data()[0]
      this.renderComparison(data)
    }
    if (this.containCity.length !== 0) {
      this.renderContainCompa(this.containCity)
    }

    this.chart.gCities.select('text')
      .attr('opacity', 1)

    this.drawLegend()
  }

  /**
   * 计算两个点的距离，单位为km
   * @param {int} lng1
   * @param {int} lat1
   * @param {int} lng2
   * @param {int} lat2
   * @returns 两点之间的距离
   */
  geoDistance(lng1, lat1, lng2, lat2) {
    let radLat1 = rad(lat1)
    let radLat2 = rad(lat2)
    let a = radLat1 - radLat2
    let b = rad(lng1) - rad(lng2)
    let s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) +
        Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)))
    s = s * 6378.137 // EARTH_RADIUS;
    s = Math.round(s * 10000) / 10000 // 输出为公里

    function rad(d) {
      return d * Math.PI / 180.0
    }
    return s
  }
}
